﻿using Lynn.Infastructure.Repository.BaseEntity;
using Microsoft.Data.SqlClient;
using Microsoft.Extensions.DependencyInjection;
using MySqlConnector;
using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations.Schema;
using System.Data;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Text;

namespace Lynn.Infastructure.Repository.Dapper
{
    [AppIgnore]
    public class DapperBaseContext : IDisposable
    {
        internal IDbConnection Connection;
        internal DataBaseType baseType;
        public DapperBaseContext Init(string connStr, DataBaseType baseType)
        {
            this.baseType = baseType;
            switch (baseType)
            {
                default: break;
                case DataBaseType.MySql: Connection = new MySqlConnection(connStr); break;
                case DataBaseType.SqlServer: Connection = new SqlConnection(connStr); break;
            }
            return this;
        }
        internal string ReplaceSQL<T>(string sql)
        {
            if (sql.StartsWith("UPDATE", StringComparison.OrdinalIgnoreCase))
            {
                sql = sql.Replace("update ", "UPDATE ", StringComparison.OrdinalIgnoreCase);
                if (typeof(T).IsAssignableFrom(typeof(IRowVersion)))
                {
                    sql = sql.Replace(" SET ", " SET RowVersion = RowVersion+1, ", StringComparison.OrdinalIgnoreCase);
                    sql = sql.Replace(" WHERE ", " WHERE RowVersion = @RowVersion AND ", StringComparison.OrdinalIgnoreCase);
                }
                if (typeof(T).IsAssignableFrom(typeof(IUpdateTime)))
                {
                    sql = sql.Replace(" SET ", " SET UpdateTime = current_timestamp(), ", StringComparison.OrdinalIgnoreCase);
                }
                if (baseType == DataBaseType.MySql)
                {
                    sql = sql + " LIMIT 1 ";//保险点，即便锁也只锁一条
                }
            }
            if (sql.StartsWith("SELECT", StringComparison.OrdinalIgnoreCase) && typeof(T).IsAssignableFrom(typeof(IDel)))
            {
                sql = sql.Replace(" WHERE ", $" WHERE Del = 0 AND ", StringComparison.OrdinalIgnoreCase);
            }
            //全文搜索 包裹逻辑删除
            if (sql.StartsWith("NSELECT", StringComparison.OrdinalIgnoreCase))
            {
                sql = sql.Replace("NSELECT", "SELECT", StringComparison.OrdinalIgnoreCase);
            }
            return sql;
        }
        internal string Set<T>(T entity) where T : class
        {
            string[] array;
            var list = typeof(T).GetProperties().Where(x => x.GetValue(entity) != null).ToList();
            if (entity is Entity e)
            {
                list = list.Where(x => x.Name != nameof(e.Id)).ToList();
            }
            if (entity is IRowVersion row)
            {
                list = list.Where(x => x.Name != nameof(row.RowVersion)).ToList();
            }
            array = list.Select(x => $"{x.Name} = @{x.Name}").ToArray();
            return string.Join(",", array);
        }
        internal string Where<T>(Expression<Func<T, bool>> where) where T : class
        {
            return FromExpression(where);
        }
        internal string Order<T>(Expression<Func<T, object>> order) where T : class
        {
            return FromExpression(order);
        }
        internal string TableName<T>() where T : class
        {
            var atr = typeof(T).GetCustomAttribute<TableAttribute>();
            if (atr != null)
            {
                return atr.Name;
            }
            return typeof(T).Name;
        }
        public void Dispose()
        {
            Connection?.Close();
            Connection?.Dispose();
        }
        #region Lambda转换
        /// <summary>
        /// 从表达式获取sql
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="func"></param>
        /// <returns></returns>
        string FromExpression<T>(Expression<Func<T, bool>> func)
        {
            if (func != null && func.Body is BinaryExpression be)
            {
                return BinarExpressionProvider(be.Left, be.Right, be.NodeType);
            }
            else
            {
                return ExpressionRouter(func.Body);
            }
        }
        string FromExpression<T>(Expression<Func<T, object>> func)
        {
            if (func != null && func.Body is BinaryExpression be)
            {
                return BinarExpressionProvider(be.Left, be.Right, be.NodeType);
            }
            else
            {
                return ExpressionRouter(func.Body);
            }
        }
        /// <summary>
        /// 拆分、拼接sql
        /// </summary>
        /// <param name="left"></param>
        /// <param name="right"></param>
        /// <param name="type"></param>
        /// <returns></returns>
        string BinarExpressionProvider(Expression left, Expression right, ExpressionType type)
        {
            string sb = "(";
            sb += ExpressionRouter(left);
            sb += ExpressionTypeCast(type);
            string tmpStr = ExpressionRouter(right);
            if (tmpStr == "null")
            {
                if (sb.EndsWith(" =")) sb = sb.Substring(0, sb.Length - 2) + " is null";
                else if (sb.EndsWith("<>")) sb = sb.Substring(0, sb.Length - 2) + " is not null";
            }
            else sb += tmpStr;
            return sb += ")";
        }

        /// <summary>
        /// 拆分、拼接sql
        /// </summary>
        /// <param name="exp"></param>
        /// <returns></returns>
        string ExpressionRouter(Expression exp)
        {
            string sb = string.Empty;
            if (exp is BinaryExpression be)
            {
                return BinarExpressionProvider(be.Left, be.Right, be.NodeType);
            }
            else if (exp is MemberExpression me)
            {
                return Eval(me).ToString();
            }
            else if (exp is NewArrayExpression ae)
            {
                StringBuilder tmpstr = new StringBuilder();
                foreach (Expression ex in ae.Expressions)
                {
                    tmpstr.Append(ExpressionRouter(ex));
                    tmpstr.Append(",");
                }
                return tmpstr.ToString(0, tmpstr.Length - 1);
            }
            else if (exp is MethodCallExpression mce)
            {
                var type = mce.Method.Name;
                switch (type)
                {
                    case "NotIn":
                    case "In":
                        var methods = type == "In" ? "IN" : "NOT IN";
                        return $"{ExpressionRouter(mce.Arguments[0])} {methods} {ExpressionRouter(mce.Arguments[1])}";
                    default:
                        break;
                }
            }
            else if (exp is ConstantExpression ce)
            {
                if (ce.Value == null)
                    return "null";
                else if (ce.Value is ValueType)
                    return ce.Value.ToString();
                else if (ce.Value is string || ce.Value is char)
                    return string.Format("'{0}'", ce.Value.ToString());
                else if (ce.Value is DateTime dt)
                    return string.Format("'{0}'", dt.ToString("yyyy-MM-dd HH:mm:ss"));
            }
            else if (exp is UnaryExpression)
            {
                UnaryExpression ue = ((UnaryExpression)exp);
                return ExpressionRouter(ue.Operand);
            }

            return null;
        }

        /// <summary>
        /// 介绍表达式树节点的节点类型 转换为 sql关键字
        /// </summary>
        /// <param name="type"></param>
        /// <returns></returns>
        string ExpressionTypeCast(ExpressionType type)
        {
            switch (type)
            {
                case ExpressionType.And:
                    return " & ";
                case ExpressionType.AndAlso:
                    return " AND ";
                case ExpressionType.Equal:
                    return " =";
                case ExpressionType.GreaterThan:
                    return " >";
                case ExpressionType.GreaterThanOrEqual:
                    return ">=";
                case ExpressionType.LessThan:
                    return "<";
                case ExpressionType.LessThanOrEqual:
                    return "<=";
                case ExpressionType.NotEqual:
                    return "<>";
                case ExpressionType.Or:
                    return " | ";
                case ExpressionType.OrElse:
                    return " Or ";
                case ExpressionType.Add:
                case ExpressionType.AddChecked:
                    return "+";
                case ExpressionType.Subtract:
                case ExpressionType.SubtractChecked:
                    return "-";
                case ExpressionType.Divide:
                    return "/";
                case ExpressionType.Multiply:
                case ExpressionType.MultiplyChecked:
                    return "*";
                default:
                    return null;
            }
        }
        /// <summary>
        /// 获取值
        /// </summary>
        /// <param name="member"></param>
        /// <returns></returns>
        object Eval(MemberExpression member)
        {
            if (member.Expression.NodeType == ExpressionType.Parameter)
            {
                return member.Member.Name;
            }
            UnaryExpression cast = Expression.Convert(member, typeof(object));
            object obj = Expression.Lambda<Func<object>>(cast).Compile().Invoke();
            return Formart(obj);
        }
        object Formart(object obj)
        {
            var type = obj.GetType().Name;
            switch (type)
            {
                case "Int64":
                    return Convert.ToInt64(obj);
                case "Decimal ": return Convert.ToDecimal(obj);
                case "Int32": return Convert.ToInt32(obj);
                case "DateTime": return $"'{ Convert.ToDateTime(obj).ToString("yyyy-MM-dd HH:mm:ss")}'";
                case "String": return $"'{ Convert.ToString(obj)}'";
                case "Char": return $"'{ Convert.ToChar(obj)}'";
                case "Boolean": return Convert.ToBoolean(obj);
                case "List`1":
                    List<object> data = new List<object>();
                    var list = obj as IEnumerable;
                    string sql = string.Empty;
                    foreach (var item in list)
                    {
                        data.Add(Formart(item));
                    }
                    sql = $"({string.Join(",", data)})";
                    return sql;
                default: return obj;
            }
        }
        #endregion
    }
}
